/***
 * @Author: 玄绮 shijiahansi@qq.com
 * @Date: 2024-05-16 15:38:10
 * @LastEditors: 玄绮 shijiahansi@qq.com
 * @LastEditTime: 2024-05-16 15:40:58
 * @FilePath: \boost_chat\src\client\app\Client.h
 * @Description:
 * @
 * @Copyright (c) 2024 by 玄绮, All Rights Reserved.
 */

#include <string>
#include <vector>
#if !defined(__CLIENT_H__)
#    define __CLIENT_H__

#    include "data/Message.pb.h"
#    include "data/UserInfo.pb.h"

#    include <ChatClient.h>
#    include <boost/algorithm/string/classification.hpp>
#    include <boost/algorithm/string/split.hpp>
#    include <boost/asio.hpp>
#    include <boost/asio/io_context.hpp>
#    include <boost/asio/ip/address.hpp>
#    include <boost/asio/ip/tcp.hpp>
#    include <boost/asio/posix/stream_descriptor.hpp>
#    include <boost/asio/read_at.hpp>
#    include <boost/asio/read_until.hpp>
#    include <boost/asio/streambuf.hpp>
#    include <boost/asio/windows/stream_handle.hpp>
#    include <boost/asio/write.hpp>
#    include <boost/format.hpp>
#    include <boost/make_shared.hpp>
#    include <boost/program_options.hpp>
#    include <boost/program_options/options_description.hpp>
#    include <boost/program_options/parsers.hpp>
#    include <boost/program_options/value_semantic.hpp>
#    include <boost/program_options/variables_map.hpp>
#    include <boost/shared_ptr.hpp>
#    include <boost/smart_ptr/enable_shared_from_this.hpp>
#    include <boost/smart_ptr/make_shared_array.hpp>
#    include <boost/smart_ptr/make_shared_object.hpp>
#    include <boost/smart_ptr/shared_ptr.hpp>
#    include <boost/system/detail/error_code.hpp>
#    include <string>
#    include <string_view>

using namespace proto::packge;
using namespace boost;
using namespace boost::asio;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type  = boost::asio::ip::address;
template <
    typename AsyncReadStream,
    typename ReadToken>
void async_read(
    AsyncReadStream& iostream,
    ReadToken handler) {
    auto& ctx               = iostream.get_executor();
    Packge::shared_ptr pack = std::make_shared<Packge>();

    boost::asio::post(ctx, [&iostream, pack, handler]() {
        boost::asio::async_read(
            iostream,
            boost::asio::buffer(&pack->header, pack->HeaderLength),
            [&iostream, pack, handler](boost::system::error_code ec, std::size_t size) {
                if (ec) {
                    std::cerr << "read body:" << ec.what() << std::endl;
                    handler(ec, pack);
                    return;
                }

                auto& header = pack->header;
                if (
                    header.body_length <= 0 ||
                    header.version != proto::packge::VERSION10 ||
                    header.proto_type != proto::packge::ProtoBuf) {
                    std::cerr << "error proto: " << std::endl;
                    handler(ec, pack);
                    return;
                }

                pack->data.resize(pack->header.body_length);
                boost::asio::async_read(iostream, boost::asio::buffer(pack->data.data(), pack->header.body_length),
                    [&iostream, pack, handler](boost::system::error_code ec, std::size_t size) {
                        if (ec) {
                            std::cerr << "read body:" << ec.what() << std::endl;
                            handler(ec, pack);
                            return;
                        }
                        handler(ec, pack);
                    });
            });
    });
}

template <
    typename AsyncReadStream,
    typename ReadToken>
void async_send(
    AsyncReadStream& iostream,
    Packge::shared_ptr pack,
    ReadToken handler) {
    auto& ctx = iostream.get_executor();

    boost::asio::post(ctx, [&iostream, pack, handler]() {
        auto sendbuff = boost::make_shared<std::string>();
        pack->SerializeToString(*sendbuff);
        async_write(iostream, boost::asio::buffer(sendbuff->data(), sendbuff->size()),
            [&iostream, pack, handler](boost::system::error_code ec, std::size_t size) {
                handler(ec, size);
            });
    });
}
enum AppStatus {
    APP_Home,
    APP_Message_PTOP,
    APP_Message_GROUP,
    APP_VerificationCode
};

struct ChatRoom
{
    using UserInfoArray = std::vector<proto::data::UserInfo>;

    proto::data::ChatRoomInfo info;
    UserInfoArray _members;  // 成员

    std::string roomid() const {
        return info.roomid();
    }
    std::string userid() const {
        return info.userid();
    }
    std::string name() const {
        return info.name();
    }
    std::string sign() const {
        return info.sign();
    }
    std::string headshot() const {
        return info.headshot();
    }
};

class Client : public ChatClient {
    using UserInfoArray = std::vector<proto::data::UserInfo>;

    using endpoint_type = boost::asio::ip::tcp::endpoint;
    using address_type  = boost::asio::ip::address;
    using socket_type   = boost::asio::ip::tcp::socket;

    std::string send_buff;
    boost::shared_ptr<socket_type> sock;
    boost::asio::io_context ctx;

    std::string host;
    int port;

    bool is_login = false;
    proto::data::UserInfo myself;

private:
    std::thread t_console;
    AppStatus app_status = AppStatus::APP_Home;
    const proto::data::FriendInfo* chat_user;
    boost::shared_ptr<ChatRoom> chat_room;
    std::map<std::string, proto::data::FriendInfo> firends;
    std::map<std::string, boost::shared_ptr<ChatRoom>> rooms;

public:
    Client(std::string_view host, int port);
    void run();
    bool connect_server(std::string_view address, int port);
    bool reconnect_server(std::string_view address, int port);

protected:
    void do_read();
    virtual void handle_error(const boost::system::error_code& ec);
    virtual void handler_system_category(const boost::system::error_code& ec);

protected:
    virtual void send_packge(Packge::shared_ptr pack) override;
    virtual void onRegister(int code, String message, std::shared_ptr<UserInfo> user) override;
    virtual void onLogin(int code, String message, std::shared_ptr<UserInfo> user) override;
    virtual void onGetUserInfo(int code, String message, std::shared_ptr<UserInfo> user) override;
    virtual void onChangUserInfo(int code, String message, std::shared_ptr<UserInfo> user) override;
    virtual void onSearchAuthor(int code, String message, std::vector<std::shared_ptr<UserInfo>> users) override;
    virtual void onSearchGroup(int code, String message, std::vector<std::shared_ptr<ChatRoomInfo>> rooms) override;
    virtual void onAddFriends(int code, String message, std::shared_ptr<FriendInfo> info) override;
    virtual void onDelFriends(int code, String message, String firendsId) override;
    virtual void onCreateGroup(int code, String message, std::shared_ptr<ChatRoomInfo> room) override;
    virtual void onCreateRoom(int code, String message, std::shared_ptr<ChatRoomInfo> room) override;
    virtual void onJoinGroup(int code, String message, std::shared_ptr<ChatRoomInfo> room) override;
    virtual void onQuitGroup(int code, String message, String roomId) override;
    virtual void onSetAccount(int code, String message) override;
    virtual void onSetUserInfo(int code, String message, std::shared_ptr<UserInfo> user) override;
    virtual void onGetFriendList(int code, String message, std::vector<std::shared_ptr<FriendInfo>> users) override;
    virtual void onGetRoomList(int code, String message, std::vector<std::shared_ptr<ChatRoomInfo>> rooms) override;
    virtual void onGetRoomMembers(int code, String message, String roomId, std::vector<std::shared_ptr<UserInfo>>) override;
    virtual void onMessage(std::shared_ptr<proto::data::Message> msg) override;

protected:
    void cmd_work(std::string& line);
    void common_cmd(std::string& line, std::vector<std::string>& list);

    void home_work(std::string& line);
    void message_work_PTOP(std::string& line);
    void message_work_GROUP(std::string& line);
    void verificationCode(std::string& line);

    void read_console();

    void start_console();
};

#endif  // __CLIENT_H__
